package neuronalenetzeelearning.view.prefuseTest;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import neuronalenetzeelearning.model.Layer1Node;
import neuronalenetzeelearning.model.Layer2Node;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.controls.ControlAdapter;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.visual.AggregateItem;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualItem;

class PopupMenuController extends ControlAdapter implements ActionListener {

    private static final String graphNodesAndEdges = "nnel";
    private static final String graphNodes = "nnel.nodes";
    private static final String graphEdges = "nnel.edges";
    private GraphEditor graphEditor;
    private Graph g;
    private Display d;
    private Visualization vis;
    private VisualItem clickedItem;
    private JPopupMenu nodePopupMenu;
    private JPopupMenu popupMenu;
    private Point2D mousePosition = new Point2D.Double();
    private VisualItem nodeVisualDummy;
    public Node nodeSourceDummy;
    public Edge edgeDummy;
    private boolean creatingEdge = false;
    private boolean editing;

    public PopupMenuController(Visualization vis, GraphEditor graphEditor) {
        this.vis = vis;
        this.graphEditor = graphEditor;
        this.g = (Graph) vis.getSourceData(graphNodesAndEdges);
        this.d = vis.getDisplay(0);
        createDummy();
        //create popupMenu for nodes
        nodePopupMenu = new JPopupMenu();
        JMenuItem delete = new JMenuItem("delete", 'd');
        JMenuItem editText = new JMenuItem("edit Name", 'a');
        JMenuItem addEdge = new JMenuItem("add Edge", 'e');
        JMenuItem addNode1 = new JMenuItem("add Layer 1 Node", 'n');
        JMenuItem addNode2 = new JMenuItem("add Layer 2 Node", 'm');
        delete.setActionCommand("node_delete");
        editText.setActionCommand("node_editText");
        addEdge.setActionCommand("node_addEdge");
        addNode1.setActionCommand("node_addNode_Layer1");
        addNode2.setActionCommand("node_addNode_Layer2");
        nodePopupMenu.addSeparator();
        nodePopupMenu.add(delete);
        nodePopupMenu.addSeparator();
        nodePopupMenu.add(editText);
        nodePopupMenu.addSeparator();
        nodePopupMenu.add(addEdge);
        nodePopupMenu.add(addNode1);
        nodePopupMenu.add(addNode2);
        delete.setMnemonic(KeyEvent.VK_D);
        editText.setMnemonic(KeyEvent.VK_A);
        addEdge.setMnemonic(KeyEvent.VK_E);
        addNode1.setMnemonic(KeyEvent.VK_N);
        addNode2.setMnemonic(KeyEvent.VK_M);
        delete.addActionListener(this);
        editText.addActionListener(this);
        addEdge.addActionListener(this);
        addNode1.addActionListener(this);
        addNode2.addActionListener(this);
        //create popupMenu for 'background'
        popupMenu = new JPopupMenu();
        addNode1 = new JMenuItem("add Layer 1 Node", 'n');
        addNode2 = new JMenuItem("add Layer 2 Node", 'm');
        addNode1.setActionCommand("addNode_Layer1");
        addNode2.setActionCommand("addNode_Layer2");
        popupMenu.addSeparator();
        popupMenu.add(addNode1);
        popupMenu.add(addNode2);
        addNode1.setMnemonic(KeyEvent.VK_N);
        addNode2.setMnemonic(KeyEvent.VK_M);
        addNode1.addActionListener(this);
        addNode2.addActionListener(this);
    }

    // ---------------------------------------------
    // --- methods for event processing
    // ---------------------------------------------
    @Override
    public void itemClicked(VisualItem item, MouseEvent e) {
        if (SwingUtilities.isRightMouseButton(e)) {
            clickedItem = item;
            //on rightclick, stop the edge creation
            if (creatingEdge) {
                stopEdgeCreation();
                return;
            }
            if (item instanceof NodeItem) {
                nodePopupMenu.show(e.getComponent(), e.getX(), e.getY());
            }
        } else if (SwingUtilities.isLeftMouseButton(e)) {
            if (creatingEdge) {
                //Check if the edge connects a Layer1Node with a Layer2Node
                Node source = edgeDummy.getSourceNode();
                Node target = (Node) item.getSourceTuple();
                if (source.get("layer").equals("Layer1Node") && target.get("layer").equals("Layer2Node")) {
                    //case1: from Layer1 to Layer2
                    g.addEdge(source, target);
                } else if (source.get("layer").equals("Layer2Node") && target.get("layer").equals("Layer1Node")) {
                    //case2: from Layer2 to Layer1
                    g.addEdge(target,source);
                } else {
                    //else: stop creation
                    stopEdgeCreation();
                    return;
                }
            } else if (item instanceof NodeItem) {
                //a node was clicked
                switch (GraphEditor.getAction()) {
                    case 1:
                        if (((NodeItem) item).get("layer").equals("Layer1Node")) {
                            addNewNode(item, "Layer1");
                        } else if (((NodeItem) item).get("layer").equals("Layer2Node")) {
                            addNewNode(item, "Layer2");
                        }
                        //	create a Layer 1node
                        break;

                    case 2:
                        //	create an edge
                        creatingEdge = true;
                        createTemporaryEdgeFromSourceToDummy(item);
                        break;
                    case 3:
                        //	rename node
                        startEditing(item);
                        break;
                    default:
                        break;
                }
            }
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (creatingEdge) {
            stopEdgeCreation();
            return;
        } else if (editing) {
            stopEditing();
        }
        if (SwingUtilities.isRightMouseButton(e)) {
            clickedItem = null;
            if (creatingEdge) {
                stopEdgeCreation();
            }
            popupMenu.show(e.getComponent(), e.getX(), e.getY());
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // called, when keyReleased events on displays textEditor are fired
        if (e.getKeyCode() == KeyEvent.VK_ENTER && editing) {
            stopEditing();
        }
    }

    @Override
    public void itemKeyReleased(VisualItem item, KeyEvent e) {
        keyReleased(e);
    }

    /**
     * called on popupMenu Action
     */
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().startsWith("node")) {
            if (e.getActionCommand().endsWith("delete")) {
                g.removeNode((Node) clickedItem.getSourceTuple());
            } else if (e.getActionCommand().endsWith("editText")) {
                startEditing(clickedItem);
            } else if (e.getActionCommand().endsWith("addEdge")) {
                creatingEdge = true;
                createTemporaryEdgeFromSourceToDummy(clickedItem);
            } else if (e.getActionCommand().endsWith("addNode_Layer1")) {
                addNewNode(clickedItem, "Layer1");
            } else if (e.getActionCommand().endsWith("addNodeLayer2")) {
                addNewNode(clickedItem, "Layer2");
            }
        } else {
            if (e.getActionCommand().equals("addNode_Layer1")) {
                addNewNode("Layer1");
//                int node = (int) (Math.random() * (g.getNodeCount() - 1));
//                Node source = g.getNode(node); //random source
//                addNewNode(source);
            } else if (e.getActionCommand().equals("addNode_Layer2")) {
                addNewNode("Layer2");
            } else {
            }
        }
    }

    // ---------------------------------------------
    // --- helper methods
    // ---------------------------------------------
    private void startEditing(VisualItem item) {
        editing = true;
        d.editText(item, "name");
    }

    private void stopEditing() {
        editing = false;
        d.stopEditing();
    }

    private void addNewNode(VisualItem source, String layer) {
        addNewNode((Node) source.getSourceTuple(), layer);
    }

    private void addNewNode(String layer) {
        if (layer.equals("Layer1")) {
            graphEditor.getController().addNode(new Layer1Node("new Layer 1 Node"));
        } else if (layer.equals("Layer2")) {
            graphEditor.getController().addNode(new Layer2Node("new Layer 2 Node"));
        }
//        Node n = g.addNode(); //create a new node
//        n.set("name", "Node " + n.getRow()); //assign a new name
//        n.set("layer", "Layer1Node");
//        n.set("threshold", "1.0");
//        g.addEdge(source, n); //add an edge from source to the new node
//        graphEditor.recalculatePositions();
    }

    private void addNewNode(Node source, String layer) {
        if (layer.equals("Layer1")) {
            graphEditor.getController().addNode(new Layer1Node("new Layer 1 Node"));
        } else if (layer.equals("Layer2")) {
            graphEditor.getController().addNode(new Layer2Node("new Layer 2 Node"));
        }
//        Node n = g.addNode(); //create a new node
//        n.set("name", "Node " + n.getRow()); //assign a new name
//        n.set("layer", "Layer1Node");
//        n.set("threshold", "1.0");
//        g.addEdge(source, n); //add an edge from source to the new node
//        graphEditor.recalculatePositions();
    }

    // ---------------------------------------------
    // --- methods for edgeCreation
    // ---------------------------------------------
    private void stopEdgeCreation() {
        creatingEdge = false;
        removeEdgeDummy();
    }

    public void removeAllDummies() {
        if (nodeSourceDummy != null) {
            g.removeNode(nodeSourceDummy);
        }
        edgeDummy = null;
        nodeSourceDummy = null;
        nodeVisualDummy = null;
    }

    private void removeEdgeDummy() {
        if (edgeDummy != null) {
            g.removeEdge(edgeDummy);
            edgeDummy = null;
        }
    }

    public VisualItem createDummy() {
        //create the dummy node for the creatingEdge mode
        nodeSourceDummy = g.addNode();
        nodeSourceDummy.set("name", "dummyNode");
        nodeSourceDummy.set("layer", "dummyLayer");
        nodeSourceDummy.set("threshold", "0.0");
        nodeVisualDummy = vis.getVisualItem(graphNodes, nodeSourceDummy);
        nodeVisualDummy.setSize(0.0);
        nodeVisualDummy.setVisible(false);
        /*
         * initially set the dummy's location. upon mouseMove events, we
         * will do that there. otherwise, the dummy would appear on top
         * left position until the mouse moves.
         * Until we use SpecifiedLayout for the nodes, it is important to set
         * the coordinate properties.
         */
        double x = d.getBounds().getCenterX();
        double y = d.getBounds().getCenterY();
        mousePosition.setLocation(x, y);
        nodeVisualDummy.setX(mousePosition.getX());
        nodeVisualDummy.setY(mousePosition.getY());
        nodeVisualDummy.set("xCoordinate", mousePosition.getX());
        nodeVisualDummy.set("yCoordinate", mousePosition.getY());
        return nodeVisualDummy;
    }

    public void removeNodeDummy() {
        g.removeNode(nodeSourceDummy);
        nodeSourceDummy = null;
        nodeVisualDummy = null;
    }

    public void createTemporaryEdgeFromSourceToDummy(Node source) {
        if (edgeDummy == null) {
            edgeDummy = g.addEdge(source, nodeSourceDummy);
        }
    }

    public void createTemporaryEdgeFromDummyToTarget(Node target) {
        if (edgeDummy == null) {
            edgeDummy = g.addEdge((Node) nodeVisualDummy.getSourceTuple(), target);
        }
    }

    /**
     * @param source the item to use as source for the dummy edge
     */
    public void createTemporaryEdgeFromSourceToDummy(VisualItem source) {
        createTemporaryEdgeFromSourceToDummy((Node) source.getSourceTuple());
    }

    /**
     * @param target the item to use as target for the dummy edge
     */
    public void createTemporaryEdgeFromDummyToTarget(VisualItem target) {
        createTemporaryEdgeFromDummyToTarget((Node) target.getSourceTuple());
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        //necessary, if you have no dummy and this ControlAdapter is running
        if (nodeVisualDummy == null) {
            return;
        }
        d.getAbsoluteCoordinate(e.getPoint(), mousePosition);
        nodeVisualDummy.setX(mousePosition.getX());
        nodeVisualDummy.setY(mousePosition.getY());
        nodeSourceDummy.set("xCoordinate", mousePosition.getX());
        nodeSourceDummy.set("yCoordinate", mousePosition.getY());
    }

    /**
     * only necessary if edge-creation is used together with aggregates and
     * the edge should move on when mousepointer moves within an aggregate
     */
    @Override
    public void itemMoved(VisualItem item, MouseEvent e) {
        if (item instanceof AggregateItem) {
            mouseMoved(e);
        }
    }
}
